package repository.impl;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import repository.ItemRepository;
import domain.Category;
import domain.Item;
import domain.Question;
import domain.User;
import domain.exceptions.DataBaseException;


@Repository
public class ItemRepositoryImpl extends AbstractHibernateRepository<Item> implements ItemRepository {
	
	@Autowired
	public ItemRepositoryImpl(SessionFactory session){
		super(session);
	}
	
	@SuppressWarnings("unchecked")
	public List<Item> getAdvancedSearchItems(String pattern, Category cat, Double minPrice, 
			Double maxPrice, Boolean state){
		Session session = getSession();
		
		Criteria crit = session.createCriteria(Item.class).add(Expression.ilike("name", "%" +  pattern + "%"))
			.add(Restrictions.eq("active", true))
			.addOrder(Order.desc("priority"))
			.addOrder(Order.asc("name"));
		
		if(minPrice!=null)
			crit.add(Restrictions.ge("precio", minPrice));
		if(maxPrice!=null)
			crit.add(Restrictions.le("precio", maxPrice));
		
		if(state!=null){
			crit = crit.add(Restrictions.eq("newItem", state));
		}
		
		if(cat != null){
			crit = crit.add(Restrictions.eq("category", cat));
		}
			
		return crit.list();
	}
	
	public void newItem(Item item) {
		// ACLARACION: No hice por default que en el constructor de item se ponga active
		// true, porque habia leido algo en inet que decia que la mejor forma
		// de setear lo que antes en la base de datos tenia valores default en las
		// columnas, es hacerlo en el objeto justo antes de salvar con los setters.
		if (!existsItem(item)){
			item.setActive(true);
			save(item);
		}
	}

	public Item getItem(int id) {
		return get(Item.class, id);
	}

	@SuppressWarnings("unchecked")
	public List<Item> getItemsByCategory(Category category, String orderBy, int order){
		Session session = getSession();
		Criteria crit = session.createCriteria(Item.class)
			.add(Restrictions.eq("active", true))
			.add(Restrictions.eq("category", category))
			.addOrder(Order.desc("priority"));
		
		if (orderBy != null) {
			crit = sortResultSet(crit, orderBy, order);
		}
		
		return crit.list();
	}
	
	@SuppressWarnings("unchecked")
	public List<Item> getItemsByUser(User user, int qty/*, int idExcluded*/) {
		Session session = getSession();
		Criteria crit = session.createCriteria(Item.class)
			.add(Restrictions.eq("active", true))
			.add(Restrictions.eq("user", user));
		
		if (qty > 0) {
			crit = crit.setMaxResults(qty);
		}
		
		return crit.list();
	}
	
	@SuppressWarnings("unchecked")
	public List<Item> getItemsByUser(User user, String orderBy, int order) {
		Session session = getSession();
		Criteria crit = session.createCriteria(Item.class)
			.add(Restrictions.eq("active", true))
			.add(Restrictions.eq("user", user));
		
		if (orderBy != null) {
			crit = sortResultSet(crit, orderBy, order);
		}
		
		return crit.list();
	}
	
	private Criteria sortResultSet(Criteria crit, String orderBy, int direction) {
		if (direction == 1) {
			crit = crit.addOrder(Order.desc(orderBy));
		} else {
			crit = crit.addOrder(Order.asc(orderBy));
		}
		
		return crit;
	}
	
	@SuppressWarnings("unchecked")
	public List<Item> getAllItems(String orderBy, int direction) {
		Session session = getSession();
		Criteria crit = session.createCriteria(Item.class)
			.add(Restrictions.eq("active", true))
			.addOrder(Order.desc("priority"));
		
		if (orderBy != null) {
			sortResultSet(crit, orderBy, direction);
		}
		
		return crit.list();
	}
	
	public void addQuestion(Question question){
		save(question);
	}
	
	@SuppressWarnings("unchecked")
	private List<Item> getSomeItems(int qty, String type) throws DataBaseException {
		Session session = getSession();
		Criteria crit = session.createCriteria(Item.class)
			.add(Restrictions.eq("active", true))
			.setMaxResults(qty)
			.addOrder(Order.desc(type));
		
		return crit.list();
	}

	public List<Item> getLastItems(int qty) {
		return getSomeItems(qty, "publicationDate");
	}
	
	public List<Item> getMoreVisitedItems(int qty) {
		return getSomeItems(qty, "visitCounter");
	}

	@SuppressWarnings("unchecked")
	public List<Item> getComplaintItems() {
		Session session = getSession();
		
		List<Item> result=session.createQuery("FROM Item WHERE active=true AND complaints.size>0 ORDER BY complaints.size DESC").list();
		return result;
	}

	public void deleteItem(Item item) {
		item.setActive(false);
	}

	private boolean existsItem(Item item) {
		//JUAN
		return !find("from Item i where i.name=? and i.publicationDate=? and i.user=?", item.getName(), 
				item.getPublicationDate(), item.getUser()).isEmpty();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Item> suggestedItems(Item item) {
		List<Item> suggested = new ArrayList<Item>();
		Session session = getSession();
		String subhql = "SELECT DISTINCT p.user FROM Purchase as p " +
					 					"WHERE p.item = ?";
		
		List<User> clients = session.createQuery(subhql).setParameter(0, item).list();
		
		if (clients.isEmpty()) {
			return null;
		}
		
		String hql =	"SELECT  DISTINCT p.item.id " +
						"FROM Purchase as p WHERE NOT p.item = ? " +
						"AND p.user IN (:clients) " +
						"GROUP BY p.user.id, p.item.id " +
						"HAVING Count(p.id) >= ALL ( SELECT Count(*) " +
											"FROM Purchase as p1 " +
											"WHERE p1.user.id = p.user.id " +
											"GROUP BY p1.user.id, p1.item.id )";
		 
		Query query = session.createQuery(hql);
		query.setEntity(0, item);
		query.setParameterList("clients", clients);
		
		List<Integer> itemsid = (List<Integer>)query.list();
		
		for (Integer itemid : itemsid) {
			suggested.add(getItem(itemid));
		}
		
		return suggested;
	}
	
	public Question getQuestion(int id) {
		return (Question) getSession().get(Question.class, id);
	}
	
	@SuppressWarnings("unchecked")
	public List<Question> getUnresponsedQuestions(User user) {
		Session session = getSession();
		return session.createQuery("FROM Question WHERE item.user = ? AND (answer=null OR answer='') ORDER BY date").setEntity(0, user).list();
	}
	
	
}
